#include "wave3dview.h"
#include <cfloat>

const int scatterPointOffset = 10;

C3DView::C3DView(QWidget *parent)
    : QOpenGLWidget{parent}
{
    m_xRange = 6.f;
    m_zRange = 5.f;
    m_yRange = 4.f;

    m_GridStyle = PaintStyle(QColor(128, 128, 128), 1.0);
    m_BorderStyle = PaintStyle(QColor(255, 0, 0), 1.5);

    m_scloe = -15.0f;

    //XYZ轴交汇原点偏移量，使偏移量是轴长的一半(正负代表方向)，是为了使实际原点在坐标系的中心(空间)
    m_translate = Point3d(-m_xRange * 0.5f,-m_yRange * 0.5f,-m_zRange * 0.5f);

    //设置多采样的值 抗锯齿
    QSurfaceFormat fmt = format();
    fmt.setSamples(18);
    setFormat(fmt);

    m_valScole = Point3d(1, 1, 0.01);
}

C3DView::~C3DView()
{

}

C3DView* C3DView::setBorderStyle(PaintStyle style)
{
    m_BorderStyle = style;
    return this;
}

C3DView *C3DView::setXTextStyle(Text2DStyle style)
{
    m_xText->mColor = style.mColor;
    m_xText->mFont = style.mFont;

    //绘制的位置 三维位置
    float xstep = m_xRange / m_xText->mNum;
    for(int i = 1; i <= m_xText->mNum; i++){
        float t = xstep * i;
        m_xText->setPos(i-1, t - xstep * 0.65f, -0.25f, m_zRange + 0.15f);
    }
    return this;
}

C3DView *C3DView::setYTextStyle(Text2DStyle style)
{
    m_yText->mColor = style.mColor;
    m_yText->mFont = style.mFont;

    float ystep = m_yRange / m_yText->mNum;
    for(int i = 1; i <= m_yText->mNum; i++){
        float t = ystep * i;
        m_yText->setPos(i-1, -0.15f, t - ystep * 0.65f, 0.05f);
    }

    return this;
}

C3DView *C3DView::setZTextStyle(Text2DStyle style)
{
    m_zText->mColor = style.mColor;
    m_zText->mFont = style.mFont;
    m_zText->setText(0, "0")
        ->setText(1, "5")
        ->setText(2, "15")
        ->setText(3, "20");

    float zstep = m_zRange / m_zText->mNum;
    for(int i = 1; i <= m_zText->mNum; i++){
        float t = zstep * i;
        m_zText->setPos(i-1, -0.35f, -0.25f, t - zstep * 0.65f);
    }
    return this;
}

C3DView* C3DView::setXNum(int xNum)
{
    m_valScole.x = m_xRange / xNum;

    int step = xNum / m_xText->mNum;
    for(int i = 0; i < m_xText->mNum; i++) {
        m_xText->setText(i, QString::number(step * (i +1)));
    }
    return this;
}

void C3DView::initializeGL()
{
    initializeOpenGLFunctions();
    glClearColor(1, 1, 1, 1.0);
    glShadeModel(GL_SMOOTH);
    glClearDepth(1.0);
    //开启深度检测
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    m_textTool->initGL();
    m_xText->createTextures();
    m_yText->createTextures();
    m_zText->createTextures();
}

void C3DView::resizeGL(int w, int h)
{
    //防止height为0
    if (h == 0) h = 1;
    //重置当前的视口
    glViewport(0, 0,(GLint)w, (GLint)h);
    //选择投影矩阵
    glMatrixMode(GL_PROJECTION);
    //重置投影矩阵
    glLoadIdentity();
    //建立透视投影矩阵,基于窗口宽度和高度的m_isize 45度视角来计算
    gluPerspective(45.0, (GLfloat)w / (GLfloat)h, 0.01, 100.0);
    //选择模型观察矩阵
    glMatrixMode(GL_MODELVIEW);
    //重置模型观察矩阵
    glLoadIdentity();
}

void C3DView::paintGL()
{
    //清除屏幕和深度缓存
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //重置当前的模型观察矩阵 在实际绘制之前，需要调用glLoadIdentity()来清除当前的矩阵
    glLoadIdentity();
    //平移
    glTranslatef(0, 0, m_scloe);
    //绕原点X轴旋转， 通过修改m_rot1坐标系上下偏转角度
    glRotatef(abs(m_rot1.z), m_rot1.x, 0, 0);
    //绕原点Y轴旋转， 通过修改m_rot2坐标系左右偏转角度
    glRotatef(abs(m_rot2.z), 0, m_rot2.y, 0);
    //初始化角度
    glRotatef(5, 0, 0, 1);
    glRotatef(25, 1, 1, 0);

    //prePorcessData();
    //动态更新y轴坐标
    if (updateObj()) {
        m_yText->createTextures();
        resetUpdate();
    }

    glPushMatrix();
    {
        glTranslatef(m_translate.x, m_translate.y, m_translate.z);
        paintPlan();
        paintBorder();
        paintGrid();
        paintAxisText();
    }
    glPopMatrix();
    glFlush();
}
void C3DView::paintBorder()
{
    glLineWidth(m_BorderStyle.lineWidth);
    glColor3f(m_BorderStyle.colorR, m_BorderStyle.colorG, m_BorderStyle.colorB);
    glBegin(GL_LINE_LOOP);
    glEnable(GL_LINE_SMOOTH);
    {
        glVertex3f(0, 0, 0);
        glVertex3f(0, m_yRange, 0);
        glVertex3f(m_xRange, m_yRange, 0);
        glVertex3f(m_xRange, 0, 0);
        glVertex3f(0, 0, 0);
        glVertex3f(0, 0, m_zRange);
        glVertex3f(m_xRange, 0, m_zRange);
        glVertex3f(m_xRange, 0, 0);
    }
    glEnd();

    glBegin(GL_LINE_STRIP);
    {
        //还有一个映射关系  0, m_xRange[1] 分成100份， 映射到0 - 2PI
        static int stepNum = 100;
        static float step = 2.0 * M_PI / stepNum;
        static float xScale = m_xRange / (2.0 * M_PI);
        static float yScale = m_yRange * 0.5f;
        //qDebug() << scale;
        for (int i = 0; i <= stepNum; ++i) {
            float x = step * i;
            glVertex3f(x * xScale, (sin(x) + 1.0f) * yScale, 0);
        }
    }
    glEnd();
}
void C3DView::paintGrid()
{
    glLineWidth(m_GridStyle.lineWidth);
    glColor3f(m_GridStyle.colorR, m_GridStyle.colorG, m_GridStyle.colorB);
    static float xstep = m_xRange / m_xText->mNum;
    static float ystep = m_yRange / m_yText->mNum;
    static float zstep = m_zRange / m_zText->mNum;
    float t = 0;
    glBegin(GL_LINES);
    {
        glEnable(GL_LINE_SMOOTH);
        for(int i = 1; i < m_xText->mNum; i++){
            t = xstep * i;
            glVertex3f(t, 0, 0);
            glVertex3f(t, m_yRange, 0);
            glVertex3f(t, 0, 0);
            glVertex3f(t, 0, m_zRange);

        }
        for(int i = 1; i < m_yText->mNum; i++){
            t = ystep * i;
            glVertex3f(0, t, 0);
            glVertex3f(m_xRange, t, 0);
        }
        for(int i = 1; i < m_zText->mNum; i++){
            t = zstep * i;
            glVertex3f(0, 0, t);
            glVertex3f(m_xRange, 0, t);
        }
    }
    glEnd();
}

void C3DView::paintAxisText()
{
    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wx, wy, wz;
    //求绘制的三维坐标， 然后投影到屏幕
    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);

    for(int i = 0; i < m_xText->getNumAndPaint(); i++) {
        CTexturePoint* texture = m_xText->mTexture[i];
        if (texture == NULL){
            continue;
        }
        //获取文字对应的屏幕坐标
        gluProject(texture->x, texture->y, texture->z, modelview, projection, viewport, &wx, &wy, &wz);
        //绘制文字
        m_textTool->drawTextLT(texture, wx, wy);
    }

    for(int i = 0; i < m_yText->getNumAndPaint(); i++) {
        CTexturePoint* texture = m_yText->mTexture[i];
        if (texture == NULL){
            continue;
        }
        gluProject(texture->x, texture->y, texture->z, modelview, projection, viewport, &wx, &wy, &wz);
        m_textTool->drawTextRT(texture, wx, wy);
    }

    for(int i = 0; i < m_zText->getNumAndPaint(); i++) {
        CTexturePoint* texture = m_zText->mTexture[i];
        if (texture == NULL){
            continue;
        }
        gluProject(texture->x, texture->y, texture->z, modelview, projection, viewport, &wx, &wy, &wz);
        m_textTool->drawTextLT(texture, wx, wy);
    }
}

void C3DView::paintPlan()
{
    glBegin(GL_QUADS);
    {
        //设置颜色和透明度
        glColor4f(0.8, 0.8, 0.8, 0.5);
        glVertex3f(0, 0, 0);
        glVertex3f(m_xRange, 0, 0);
        glVertex3f(m_xRange, 0, m_zRange);
        glVertex3f(0, 0, m_zRange);
    }
    glEnd();
}

void C3DView::mousePressEvent(QMouseEvent *event)
{
    QOpenGLWidget::mousePressEvent(event);

    QPoint pos = event->pos();
    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wX, wY, wZ;

    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);

    // 将鼠标坐标转换为世界坐标
    gluUnProject(pos.x(), pos.y(), 0, modelview, projection, viewport, &wX, &wY, &wZ);
    m_press.x = wX;
    m_press.y = wY;
    m_press.z = 0;
}

void C3DView::mouseReleaseEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseReleaseEvent(event);
    m_press.x = 0;
    m_press.y = 0;
    m_press.z = 0;
}

void C3DView::mouseMoveEvent(QMouseEvent *event)
{
    QOpenGLWidget::mouseMoveEvent(event);

    QPoint pos = event->pos();

    static GLdouble modelview[16];
    static GLdouble projection[16];
    static GLint viewport[4];
    static GLdouble wx, wy, wz;

    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
    glGetDoublev(GL_PROJECTION_MATRIX, projection);
    glGetIntegerv(GL_VIEWPORT, viewport);
    // 将鼠标坐标转换为世界坐标
    gluUnProject(pos.x(), pos.y(), 0, modelview, projection, viewport, &wx, &wy, &wz);
    if(event->buttons() & Qt::LeftButton) {
        m_rot1.x += (wy-m_press.y)*100;
        m_rot1.z = m_rot1.x;
        m_rot2.y += (wx-m_press.x)*100;
        m_rot2.z = m_rot2.y;
    }

    if(event->buttons() & Qt::RightButton) {
        m_translate.x += (wx-m_press.x)*10.0;
        m_translate.y += -(wy-m_press.y)*10.0;
    }
    m_press.x = wx;
    m_press.y = wy;
    m_press.z = 0;
    update();
}

void C3DView::wheelEvent(QWheelEvent *event)
{
    QOpenGLWidget::wheelEvent(event);
    if (event->angleDelta().y() > 0) {
        m_scloe += m_scloe < -1 ? 1 : 0;
    } else if (event->angleDelta().y() < 0) {
        m_scloe -= m_scloe > -100 ? 1 : 0;
    }

    update();
}

CAxisTextManage::CAxisTextManage()
{
    m_textTool = new CText2DTool();
    m_xText = NULL;
    m_yText = NULL;
    m_zText = NULL;
}

CAxisTextManage::~CAxisTextManage()
{
    if (m_xText != NULL){
        delete m_xText;
        m_xText = NULL;
    }
    if (m_yText != NULL){
        delete m_yText;
        m_yText = NULL;
    }
    if (m_zText != NULL){
        delete m_zText;
        m_zText = NULL;
    }

    if (m_textTool != NULL){
        delete m_textTool;
        m_textTool = NULL;
    }
}

void CAxisTextManage::initAxis(int vx, int vy, int vz)
{
    m_xText = new CAxisText(vx);
    m_yText = new CAxisText(vy);
    m_zText = new CAxisText(vz);
}

CDataManage::CDataManage()
{
    m_waveNum = 100;
    m_yMax = FLT_MIN;
    m_bUpdateMax = false;
    ll = &CLineLegend::getInstance();
    pl = &CPointLegend::getInstance();
}

CDataManage::~CDataManage()
{
}

void CDataManage::clearData()
{
    m_yMax = FLT_MIN;
    m_bUpdateMax = false;
}
